package leetcode.backtrace;

/*

树是一个无向图，其中任何两个顶点只通过一条路径连接。 换句话说，一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树，标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表（每一个边都是一对标签），其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时，设结果树的高度为 h 。在所有可能的树中，具有最小高度的树（即，min(h)）被称为 最小高度树 。

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。
 

示例 1：


输入：n = 4, edges = [[1,0],[1,2],[1,3]]
输出：[1]
解释：如图所示，当根是标签为 1 的节点时，树的高度是 1 ，这是唯一的最小高度树。
示例 2：


输入：n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出：[3,4]
示例 3：

输入：n = 1, edges = []
输出：[0]
示例 4：

输入：n = 2, edges = [[0,1]]
输出：[0,1]
 

提示：

1 <= n <= 2 * 104
edges.length == n - 1
0 <= ai, bi < n
ai != bi
所有 (ai, bi) 互不相同
给定的输入 保证 是一棵树，并且 不会有重复的边

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/minimum-height-trees
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。

 */

import java.util.*;

public class LeetCode310_FindMinHeightTrees {

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {

        List<Integer> result = new ArrayList<>();
        Map<Integer, Integer> numHeightMap = new HashMap<>();

        int currentMin = Integer.MAX_VALUE;
        int tempHeight;
        for (int i = 0; i < n; i++){
            if (numHeightMap.containsKey(i)){
                continue;
            }
            tempHeight = height(i, edges, currentMin, numHeightMap);
            if (tempHeight <= currentMin){
                if (tempHeight < currentMin){
                    result.clear();
                }
                result.add(i);
                currentMin = tempHeight;
            }
        }

        return result;
    }

    public static int height(int n, int[][] edges, int currentMin, Map<Integer, Integer> numHeightMap){

        Stack<Integer> levelNode = new Stack<>();
        int maxHeight = 0;
        Set<Integer> useNum = new HashSet<>();

        levelNode.add(n);
        while (!levelNode.isEmpty()){

            Stack<Integer> oldLevelNode = new Stack<>();
            oldLevelNode.addAll(levelNode);

            Stack<Integer> newLevelNode = new Stack<>();
            while (!levelNode.isEmpty()){
                n = levelNode.pop();
                for (int i = 0; i < edges.length; i++){
                    if (edges[i][0] == n && !useNum.contains(edges[i][1])){
                        newLevelNode.add(edges[i][1]);
                        useNum.add(n);
                    }else if (edges[i][1] == n && !useNum.contains(edges[i][0])){
                        newLevelNode.add(edges[i][0]);
                        useNum.add(n);
                    }
                }
            }
            levelNode = newLevelNode;
            if (!levelNode.isEmpty()){
                maxHeight++;

                if (maxHeight > currentMin){

                    while (!levelNode.isEmpty()) {
                        newLevelNode = new Stack<>();
                        while (!levelNode.isEmpty()) {
                            n = levelNode.pop();

                            if(numHeightMap.containsKey(n)){
                                continue;
                            }

                            numHeightMap.put(n, maxHeight);

                            for (int i = 0; i < edges.length; i++) {
                                if (edges[i][0] == n && !useNum.contains(edges[i][1])) {
                                    newLevelNode.add(edges[i][1]);
                                    useNum.add(n);
                                } else if (edges[i][1] == n && !useNum.contains(edges[i][0])) {
                                    newLevelNode.add(edges[i][0]);
                                    useNum.add(n);
                                }
                            }
                        }
                        levelNode = newLevelNode;
                    }
                    return maxHeight;
                }
            }
        }
        return maxHeight;
    }

    public static void main(String[] args) {
        //int[][] edges = {{3,0},{3,1},{3,2},{3,4},{5,4}};
        int[][] edges = {{1,0},{1,2},{1,3}};

        new LeetCode310_FindMinHeightTrees().findMinHeightTrees(5, edges);

        int height = height(5, edges, Integer.MAX_VALUE, new HashMap<>());

        System.out.println("height = " + height);
    }
}
